package com.lzhl.imaging.ui;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;

/**
 * @author 李柱良(@柱子的日月)
 * @date 2014/8/27
 */
public class ImagingUI extends JPanel implements ActionListener, ChangeListener {

    private static final FontRenderContext FRC = new FontRenderContext(null, false, false);

    private Stroke dash = new BasicStroke(
            1f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,1f,new float[]{3,2,},0f);

    private static DecimalFormat df = new DecimalFormat();

    private Dimension titleDimension = new Dimension(100, 20);
    private Dimension valueDimension = new Dimension(60, 20);
    private Insets margin = new Insets(0, 0, 0, 0);

    private static final int JING_WU = 0;
    private static final int QIAN_JING = 1;
    private static final int HOU_JING = -1;

    private Box boxCtrl;
    private JPanel canvas;
    private Box boxStatus;

    private JSlider sdGuangQuan;
    private JSlider sdBianJiao;
    private JSlider sdDuiJiao;
    private JSlider sdMisand;

    private JSlider sdWugao;
    private JSlider sdJingWu;
    private JSlider sdQianJing;
    private JSlider sdHouJing;

    private ChangeListener bianJiaoListener;
    private ChangeListener duiJiaoListener;
    private ChangeListener jingWuListener;
    private ChangeListener qianJingListener;
    private ChangeListener houJingListener;

    private JCheckBox cbJingWu = new JCheckBox("显示");
    private JCheckBox cbQianJing = new JCheckBox("显示");
    private JCheckBox cbHouJing = new JCheckBox("显示");

    private JLabel lbGuangQuan = new JLabel();
    private JLabel lbBianJiao = new JLabel();
    private JLabel lbDuiJiao = new JLabel();
    private JLabel lbMisand = new JLabel();
    private JLabel lbWugao = new JLabel();
    private JLabel lbJingWu = new JLabel();
    private JLabel lbQianJing = new JLabel();
    private JLabel lbHouJing = new JLabel();

    private JCheckBox cbGx;
    private JCheckBox cbPingXing;
    private JCheckBox cbRuShe;
    private JCheckBox cbYanChang;
    private JCheckBox cbMiSan;

    private Painter painter = new Painter(true);

    public ImagingUI() {
        setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
        setLayout(new BorderLayout());

        boxCtrl = Box.createVerticalBox();
        add(boxCtrl, BorderLayout.NORTH);

        canvas = new JPanel(){
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                painter.setSize(getWidth(), getHeight());
                painter.paint(g);
            }
        };
        add(canvas, BorderLayout.CENTER);

        boxStatus = Box.createVerticalBox();
        add(boxStatus, BorderLayout.SOUTH);

        initCtrl();

        initJingWu();

        initOption();

        updateCtrl();
    }

    private void initCtrl() {
        JLabel lb;
        JButton btn;

        Box boxGuangQuan = Box.createHorizontalBox();
        boxCtrl.add(boxGuangQuan);
        lb = new JLabel("光圈(正比)");
        lb.setPreferredSize(titleDimension);
        boxGuangQuan.add(lb);
        sdGuangQuan = new JSlider(1, 50, 10);
        boxGuangQuan.add(sdGuangQuan);
        lbGuangQuan.setPreferredSize(valueDimension);
        boxGuangQuan.add(lbGuangQuan);


        Box boxBianJiao = Box.createHorizontalBox();
        boxCtrl.add(boxBianJiao);
        lb = new JLabel("变焦");
        lb.setPreferredSize(titleDimension);
        boxBianJiao.add(lb);
        sdBianJiao = new JSlider(10, 200, 30);
        boxBianJiao.add(sdBianJiao);
        lbBianJiao.setPreferredSize(valueDimension);
        boxBianJiao.add(lbBianJiao);

        Box boxDuiJiao = Box.createHorizontalBox();
        boxCtrl.add(boxDuiJiao);
        lb = new JLabel("对焦");
        lb.setPreferredSize(titleDimension);
        boxDuiJiao.add(lb);
        sdDuiJiao = new JSlider(0, 50000, 2668);
        boxDuiJiao.add(sdDuiJiao);
        btn = new JButton("自动对焦");
        btn.setMargin(margin);
        boxDuiJiao.add(btn);
        btn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                autoDuiJiao();
            }
        });
        boxDuiJiao.add(Box.createHorizontalStrut(5));
        lbDuiJiao.setPreferredSize(valueDimension);
        boxDuiJiao.add(lbDuiJiao);


        Box boxMisand = Box.createHorizontalBox();
        boxCtrl.add(boxMisand);
        lb = new JLabel("允许弥散圆直径");
        lb.setPreferredSize(titleDimension);
        boxMisand.add(lb);
        sdMisand = new JSlider(1, 2000, 400);
        boxMisand.add(sdMisand);
        btn = new JButton("计算景深");
        btn.setMargin(margin);
        boxMisand.add(btn);
        btn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                calcJingShen();
            }
        });
        boxMisand.add(Box.createHorizontalStrut(5));
        lbMisand.setPreferredSize(valueDimension);
        boxMisand.add(lbMisand);


        sdGuangQuan.addChangeListener(this);
        bianJiaoListener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                bianJiaoStateChanged(e);
            }
        };
        sdBianJiao.addChangeListener(bianJiaoListener);
        duiJiaoListener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                duiJiaoStateChanged(e);
            }
        };
        sdDuiJiao.addChangeListener(duiJiaoListener);
        sdMisand.addChangeListener(this);
    }

    private void initJingWu() {
        JLabel lb;

        Box boxWugao = Box.createHorizontalBox();
        boxCtrl.add(boxWugao);
        lb = new JLabel("物高");
        lb.setPreferredSize(titleDimension);
        boxWugao.add(lb);
        sdWugao = new JSlider(1, 2000, 200);
        boxWugao.add(sdWugao);
        lbWugao.setPreferredSize(valueDimension);
        boxWugao.add(lbWugao);

        Box boxQianJing = Box.createHorizontalBox();
        boxCtrl.add(boxQianJing);
        lb = new JLabel("前景距离");
        lb.setPreferredSize(titleDimension);
        boxQianJing.add(lb);
        sdQianJing = new JSlider(10, 5000, 333);
        boxQianJing.add(sdQianJing);
        cbQianJing.setSelected(false);
        cbQianJing.addActionListener(this);
//        cbQianJing.setPreferredSize(new Dimension(30, 20));
        boxQianJing.add(cbQianJing);
        lbQianJing.setPreferredSize(valueDimension);
        boxQianJing.add(lbQianJing);

        Box boxJingWu = Box.createHorizontalBox();
        boxCtrl.add(boxJingWu);
        lb = new JLabel("拍摄距离");
        lb.setPreferredSize(titleDimension);
        boxJingWu.add(lb);
        sdJingWu = new JSlider(10, 5000, 400);
        boxJingWu.add(sdJingWu);
        cbJingWu.setSelected(true);
        cbJingWu.addActionListener(this);
//        cbJingWu.setPreferredSize(new Dimension(30, 20));
        boxJingWu.add(cbJingWu);
        lbJingWu.setPreferredSize(valueDimension);
        boxJingWu.add(lbJingWu);

        Box boxHouJing = Box.createHorizontalBox();
        boxCtrl.add(boxHouJing);
        lb = new JLabel("后景距离");
        lb.setPreferredSize(titleDimension);
        boxHouJing.add(lb);
        sdHouJing = new JSlider(10, 5000, 506);
        boxHouJing.add(sdHouJing);
        cbHouJing.setSelected(false);
        cbHouJing.addActionListener(this);
//        cbHouJing.setPreferredSize(new Dimension(30, 20));
        boxHouJing.add(cbHouJing);
        lbHouJing.setPreferredSize(valueDimension);
        boxHouJing.add(lbHouJing);

        sdWugao.addChangeListener(this);
        jingWuListener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                jingWuStateChanged(e);
            }
        };
        sdJingWu.addChangeListener(jingWuListener);
        qianJingListener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                qianJingStateChanged(e);
            }
        };
        sdQianJing.addChangeListener(qianJingListener);
        houJingListener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                houJingStateChanged(e);
            }
        };
        sdHouJing.addChangeListener(houJingListener);
    }

    private void initOption() {
        JLabel lb;

        Box boxXuanXiang = Box.createHorizontalBox();
        boxCtrl.add(boxXuanXiang);
        lb = new JLabel("选项");
        lb.setPreferredSize(titleDimension);
        boxXuanXiang.add(lb);

        cbGx = new JCheckBox("显示光心线");
        cbGx.setSelected(false);
        cbGx.addActionListener(this);
        boxXuanXiang.add(cbGx);

        cbPingXing = new JCheckBox("显示平行线");
        cbPingXing.setSelected(false);
        cbPingXing.addActionListener(this);
        boxXuanXiang.add(cbPingXing);

        cbRuShe = new JCheckBox("显示入射线");
        cbRuShe.setSelected(true);
        cbRuShe.addActionListener(this);
        boxXuanXiang.add(cbRuShe);

        cbYanChang = new JCheckBox("显示延长线");
        cbYanChang.setSelected(false);
        cbYanChang.addActionListener(this);
        boxXuanXiang.add(cbYanChang);

        cbMiSan = new JCheckBox("显示弥散圆");
        cbMiSan.setSelected(false);
        cbMiSan.addActionListener(this);
        boxXuanXiang.add(cbMiSan);

        boxXuanXiang.add(Box.createHorizontalGlue());
    }

    private void updateCtrl() {
        lbBianJiao.setText(String.valueOf(sdBianJiao.getValue()));
        lbGuangQuan.setText(String.valueOf(sdGuangQuan.getValue()));
        lbDuiJiao.setText(format(sdDuiJiao.getValue() / 1000f, 3));
        lbMisand.setText(format(sdMisand.getValue() / 1000f, 3));
        lbWugao.setText(String.valueOf(sdWugao.getValue()));
        lbJingWu.setText(String.valueOf(sdJingWu.getValue()));
        lbQianJing.setText(String.valueOf(sdQianJing.getValue()));
        lbHouJing.setText(String.valueOf(sdHouJing.getValue()));
    }

    public void stateChanged(ChangeEvent e) {
        canvas.repaint();
        updateCtrl();
    }

    public void bianJiaoStateChanged(ChangeEvent e) {
        double d = (getJingLength(QIAN_JING)-getDuiJiaoLength())/2f;
        int focusLength;
        if(d - Math.floor(d) > 0) {
            focusLength = (int) Math.floor(d);
        }
        else {
            focusLength = (int) d - 1;
        }
        if(sdBianJiao.getValue() > focusLength) {
            sdBianJiao.removeChangeListener(bianJiaoListener);
            sdBianJiao.setValue(focusLength);
            sdBianJiao.addChangeListener(bianJiaoListener);
        }

        canvas.repaint();
        updateCtrl();
    }

    public void duiJiaoStateChanged(ChangeEvent e) {
        double d = (getJingLength(QIAN_JING)-getFocusLength()*2)*1000;
        int duiJiaoLength;
        if(d - Math.floor(d) > 0) {
            duiJiaoLength = (int) Math.floor(d);
        }
        else {
            duiJiaoLength = (int) d - 1;
        }
        if(sdDuiJiao.getValue() > duiJiaoLength) {
            sdDuiJiao.removeChangeListener(duiJiaoListener);
            sdDuiJiao.setValue(duiJiaoLength);
            sdDuiJiao.addChangeListener(duiJiaoListener);
        }

        canvas.repaint();
        updateCtrl();
    }

    public void jingWuStateChanged(ChangeEvent e) {
        int jiaodian = (int) Math.ceil(getDuiJiaoLength()+getFocusLength()*2);
        if(sdJingWu.getValue() < jiaodian) {
            sdJingWu.removeChangeListener(jingWuListener);
            sdJingWu.setValue(jiaodian);
            sdJingWu.addChangeListener(jingWuListener);
        }
        if(sdQianJing.getValue() > sdJingWu.getValue()) {
            sdQianJing.removeChangeListener(qianJingListener);
            sdQianJing.setValue(sdJingWu.getValue());
            sdQianJing.addChangeListener(qianJingListener);
        }
        if(sdHouJing.getValue() < sdJingWu.getValue()) {
            sdHouJing.removeChangeListener(houJingListener);
            sdHouJing.setValue(sdJingWu.getValue());
            sdHouJing.addChangeListener(houJingListener);
        }

        canvas.repaint();
        updateCtrl();
    }

    public void qianJingStateChanged(ChangeEvent e) {
        int jiaodian = (int) Math.ceil(getDuiJiaoLength()+getFocusLength()*2);
        if(sdQianJing.getValue() > sdJingWu.getValue()) {
            sdQianJing.setValue(sdJingWu.getValue());
        }
        if(sdQianJing.getValue() < jiaodian) {
            sdQianJing.setValue(jiaodian);
        }

        canvas.repaint();
        updateCtrl();
    }

    public void houJingStateChanged(ChangeEvent e) {
        if(sdHouJing.getValue() < sdJingWu.getValue()) {
            sdHouJing.setValue(sdJingWu.getValue());
        }

        canvas.repaint();
        updateCtrl();
    }

    public void actionPerformed(ActionEvent e) {
        canvas.repaint();
    }

    private void autoDuiJiao() {
        cbJingWu.setSelected(true);

        //因绘画合并没有触发paintDiagrammatic，所以new一个BufferedImage来计算
        BufferedImage bi = new BufferedImage(
                canvas.getWidth(), canvas.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = bi.createGraphics();

        Painter painter = new Painter(false);
        painter.setSize(canvas.getWidth(), canvas.getHeight());
        painter.paint(g2d);

        int init = sdDuiJiao.getValue();
        int index = init;
        double wucha = Math.abs(painter.duijiaoWucha);
        //向前
        for(int i = init+1; i <= sdDuiJiao.getMaximum(); i++) {
            sdDuiJiao.setValue(i);
            g2d.translate(0, 0);
            painter.paint(g2d);
            if(Math.abs(painter.duijiaoWucha) <= wucha) {
                index = i;
                wucha = Math.abs(painter.duijiaoWucha);
            }
            else {
                break;
            }
        }
        //向后
        for(int i = init-1; i >= sdDuiJiao.getMinimum(); i--) {
            sdDuiJiao.setValue(i);
            g2d.translate(0, 0);
            painter.paint(g2d);
            if(Math.abs(painter.duijiaoWucha) <= wucha) {
                index = i;
                wucha = Math.abs(painter.duijiaoWucha);
            }
            else {
                break;
            }
        }

        sdDuiJiao.setValue(index);
        canvas.repaint();
    }

    private void calcJingShen() {
        cbJingWu.setSelected(true);
        cbQianJing.setSelected(true);
        cbHouJing.setSelected(true);

        autoDuiJiao();

        BufferedImage bi = new BufferedImage(
                canvas.getWidth(), canvas.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = bi.createGraphics();

        Painter painter = new Painter(false);
        painter.setSize(canvas.getWidth(), canvas.getHeight());
        painter.paint(g2d);

        //前景
        int init = sdJingWu.getValue();
        int index = init;
        double chazhi = getAllowMisan();
        for(int i = init-1; i >= sdQianJing.getMinimum(); i--) {
            sdQianJing.setValue(i);
            g2d.translate(0, 0);
            painter.paint(g2d);
            if(painter.dQianMiSan < painter.dMiSan) {
                continue;
            }
            if(painter.dQianMiSan > getAllowMisan()) {
                break;
            }
            if(chazhi > Math.abs(painter.dQianMiSan-getAllowMisan())) {
                index = i;
                chazhi = Math.abs(painter.dQianMiSan-getAllowMisan());
            }
            else {
                break;
            }
        }
        sdQianJing.setValue(index);

        //后景
        init = sdJingWu.getValue();
        index = init;
        chazhi = getAllowMisan();
        for(int i = init+1; i <= sdHouJing.getMaximum(); i++) {
            sdHouJing.setValue(i);
            g2d.translate(0, 0);
            painter.paint(g2d);
            if(painter.dHouMiSan < painter.dMiSan) {
                continue;
            }
            if(painter.dHouMiSan > getAllowMisan()) {
                break;
            }
            if(chazhi > Math.abs(painter.dHouMiSan-getAllowMisan())) {
                index = i;
                chazhi = Math.abs(painter.dHouMiSan-getAllowMisan());
            }
            else {
                break;
            }
        }
        sdHouJing.setValue(index);

        canvas.repaint();
    }

    private double getGuangQuan() {
        return  sdGuangQuan.getValue();
    }

    private double getFocusLength() {
        return sdBianJiao.getValue();
    }

    private double getDuiJiaoLength() {
        return sdDuiJiao.getValue()/1000f;
    }

    private double getAllowMisan() {
        return sdMisand.getValue()/1000f;
    }

    private double getJingHeight() {
        return sdWugao.getValue();
    }

    private double getJingLength(int jing) {
        if(jing == JING_WU) {
            return sdJingWu.getValue();
        }
        else if(jing == QIAN_JING) {
            return sdQianJing.getValue();
        }
        else if(jing == HOU_JING) {
            return sdHouJing.getValue();
        }
        else {
            throw new IllegalArgumentException("错误参数");
        }
    }

    //******************************************************************************************************************

    class Painter {
        boolean doPaint;
        int width = 0;
        int height = 0;

        double duijiaoWucha = 0;
        double dMiSan = 0;
        double dQianMiSan = 0;
        double dHouMiSan = 0;

        Painter(boolean doPaint) {
            this.doPaint = doPaint;
        }

        public void setSize(int width, int height) {
            this.width = width;
            this.height = height;
        }

        public void paint(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            paintZuobiao(g2d);

            paintCmos(g2d);

            paintGlass(g2d);

            paintAllObject(g2d);
        }

        private void paintZuobiao(Graphics2D g2d) {
            //画光轴
            drawLine(g2d, 0, height*2/3f, width, height*2/3f);
            g2d.translate(150, height * 2 / 3f);
        }

        /**
         * 感光元件
         * @param g2d
         */
        private void paintCmos(Graphics2D g2d) {
            Stroke stroke = g2d.getStroke();
            g2d.setStroke(dash);
            drawLine(g2d, 0, -280, 0, 300);
            g2d.setStroke(stroke);

            double size = 60;
            drawLine(g2d, 0, 0-size, 0, size);

            drawString(g2d, "cmos", 0, -280, true);
        }

        private void paintGlass(Graphics2D g2d) {
            double focusLength = getFocusLength();
            double duijiaoLength = getDuiJiaoLength();
            double xGx = duijiaoLength + focusLength;

            //透镜
            drawArrowLine(g2d, xGx, -60, xGx, 60, 8);
            drawString(g2d, "透镜", xGx, -300, true);

            //光圈
            double guanquan = getGuangQuan();
            drawLine(g2d, xGx-5, 0-guanquan, xGx+5, 0-guanquan);
            drawLine(g2d, xGx-5, 0-guanquan-1, xGx+5, 0-guanquan-1);
            drawLine(g2d, xGx-5, guanquan, xGx+5, guanquan);
            drawLine(g2d, xGx-5, guanquan+1, xGx+5, guanquan+1);

            Stroke stroke = g2d.getStroke();
            g2d.setStroke(dash);
            double size = -100;

            drawLine(g2d, xGx, -300, xGx, 300);

            drawLine(g2d, xGx, guanquan+1, xGx+40, guanquan+1+40);
            drawString(g2d, "光圈"+sdGuangQuan.getValue(), xGx+40, guanquan+1+40, false);

            //对焦
            drawArrowLine(g2d, 0, -90, duijiaoLength, -90, 4);
            drawLine(g2d, duijiaoLength/2f, -90, duijiaoLength/2f+30, -90-30);
            drawString(g2d, "对焦"+format(duijiaoLength, 3), duijiaoLength/2f+30, -90-30, true);

            //焦点竖线
            drawLine(g2d, xGx - focusLength, 0, xGx - focusLength, size);
            drawLine(g2d, xGx+focusLength, 0, xGx+focusLength, size);
            drawLine(g2d, xGx+focusLength*2, 0, xGx+focusLength*2, size);

            //焦距横线
            drawArrowLine(g2d, xGx-focusLength, -80, xGx, -80, 4);
            drawArrowLine(g2d, xGx, -80, xGx+focusLength, -80, 4);
            drawArrowLine(g2d, xGx+focusLength, -80, xGx+focusLength*2, -80, 4);

            double xJiaoJu = xGx+focusLength/2f+40;
            if(xJiaoJu < duijiaoLength/2f+30+60) {
                xJiaoJu = duijiaoLength/2f+30+60;
            }
            drawLine(g2d, xGx+focusLength/2f, -80, xJiaoJu, -90-30);
            drawString(g2d, "焦距"+sdBianJiao.getValue(), xJiaoJu, -90-30, true);

            g2d.setStroke(stroke);

            drawString(g2d, "f", xGx-focusLength/2f, -80, true);
            drawString(g2d, "f", xGx+focusLength/2f, -80, true);
            drawString(g2d, "f", xGx+focusLength+focusLength/2f, -80, true);
        }

        private void paintAllObject(Graphics2D g2d) {
            if(cbQianJing.isSelected()) {
                g2d.setColor(Color.RED);
                paintObject(g2d, QIAN_JING);
                paintGlassLight(g2d, QIAN_JING);
            }

            if(cbJingWu.isSelected()) {
                g2d.setColor(Color.GREEN);
                paintObject(g2d, JING_WU);
                paintGlassLight(g2d, JING_WU);
            }

            if(cbHouJing.isSelected()) {
                g2d.setColor(Color.BLUE);
                paintObject(g2d, HOU_JING);
                paintGlassLight(g2d, HOU_JING);
            }

            paintJingShen(g2d);
        }

        private void paintObject(Graphics2D g2d, int jing) {
            double x = getJingLength(jing);
            double y = 0 - getJingHeight();
            drawLine(g2d, x, 0, x, y);

            String text = "物";
            if(jing == QIAN_JING) {
                text = "前";
            }
            else if(jing == HOU_JING) {
                text = "后";
            }

            drawString(g2d, text, x, y, true);

            if(jing == JING_WU) {
                Stroke stroke = g2d.getStroke();
                g2d.setStroke(dash);

                drawArrowLine(g2d, getDuiJiaoLength()+getFocusLength(), -20,
                        x, -20, 8);
                double wuju = x - getDuiJiaoLength()-getFocusLength();
                drawString(g2d, "物距"+format(wuju, 3), (getDuiJiaoLength()+getFocusLength()+x)/2f, -24);

                g2d.setStroke(stroke);
            }
        }

        private void paintGlassLight(Graphics2D g2d, int jing) {
            double jingLength = getJingLength(jing);
            double jingHeight = getJingHeight();
            double focusLength = getFocusLength();
            double duijiaoLength = getDuiJiaoLength();
            double guangQuan = getGuangQuan();

            //光心
            double xGx = duijiaoLength + focusLength;
            double yGx = 0;

            //焦点
            double xFocus = duijiaoLength;
            double yFocus = 0;

            //物点
            double xObject = jingLength;
            double yObject = 0 - jingHeight;

            //平行光入射点
            double xPx = xGx;
            double yPx = 0 - jingHeight;

            //上光圈点
            double xShangGq = xGx;
            double yShangGq = 0-guangQuan;

            //下光圈点
            double xXiaGq = xGx;
            double yXiaGq = guangQuan;

            //象点
            Point2D.Double image = calcIntersectionPoint(xObject, yObject, xGx, yGx, xPx, yPx, xFocus, yFocus);
            //当数值太小时，画图时点出现在-1上了
            Point2D.Double imageFix = new Point2D.Double(image.x, image.y);
            if(imageFix.x < 0 && imageFix.x > -0.001) {
                imageFix.x = 0;
            }

            //光心线交点
            double xGxCmos = 0;
            double yGxCmos = calcY(xObject, yObject, xGx, yGx, xGxCmos);
            double xGxYanChang = -1000;
            double yGxYanChang = calcY(xObject, yObject, xGx, yGx, xGxYanChang);

            //平行线交点
            double xPxCmos = 0;
            double yPxCmos = calcY(xPx, yPx, xFocus, yFocus, xPxCmos);
            double xPxYanChang = -1000;
            double yPxYanChang = calcY(xPx, yPx, xFocus, yFocus, xPxYanChang);

            //上光圈交点
            double xShangGqCmos = 0;
            double yShangGqCmos = calcY(xShangGq, yShangGq, image.x, image.y, xShangGqCmos);
            double xShangGqYanChang = -1000;
            double yShangGqYanChang = calcY(xShangGq, yShangGq, image.x, image.y, xShangGqYanChang);

            //下光圈交点
            double xXiaGqCmos = 0;
            double yXiaGqCmos = calcY(xXiaGq, yXiaGq, image.x, image.y, xXiaGqCmos);
            double xXiaGqYanChang = -1000;
            double yXiaGqYanChang = calcY(xXiaGq, yXiaGq, image.x, image.y, xXiaGqYanChang);

            Stroke stroke = g2d.getStroke();

            //过光心光线
            if(cbGx.isSelected()) {
                drawLine(g2d, xObject, yObject, xGxCmos, yGxCmos);
                g2d.setStroke(dash);
                if(image.x < xGxCmos) {
                    drawLine(g2d, xGxCmos, yGxCmos, imageFix.x, imageFix.y);
                }
                if(cbYanChang.isSelected()) {
                    if(image.x < xGxCmos) {
                        drawLine(g2d, image.x, image.y, xGxYanChang, yGxYanChang);
                    }
                    else {
                        drawLine(g2d, xGxCmos, yGxCmos, xGxYanChang, yGxYanChang);
                    }
                }
            }

            if(cbRuShe.isSelected()) {
                g2d.setStroke(stroke);
                //上光圈线
                drawLine(g2d, xObject, yObject, xShangGq, yShangGq);
                drawLine(g2d, xShangGq, yShangGq, xShangGqCmos, yShangGqCmos);
                g2d.setStroke(dash);
                if(image.x < xShangGqCmos) {
                    drawLine(g2d, xShangGqCmos, yShangGqCmos, imageFix.x, imageFix.y);
                }
                if(cbYanChang.isSelected()) {
                    if(image.x < xShangGqCmos) {
                        drawLine(g2d, image.x, image.y, xShangGqYanChang, yShangGqYanChang);
                    }
                    else {
                        drawLine(g2d, xShangGqCmos, yShangGqCmos, xShangGqYanChang, yShangGqYanChang);
                    }
                }

                //下光圈线
                g2d.setStroke(stroke);
                drawLine(g2d, xObject, yObject, xXiaGq, yXiaGq);
                drawLine(g2d, xXiaGq, yXiaGq, xXiaGqCmos, yXiaGqCmos);
                g2d.setStroke(dash);
                if(image.x < xXiaGqCmos) {
                    drawLine(g2d, xXiaGqCmos, yXiaGqCmos, imageFix.x, imageFix.y);
                }
                if(cbYanChang.isSelected()) {
                    if(image.x < xXiaGqCmos) {
                        drawLine(g2d, image.x, image.y, xXiaGqYanChang, yXiaGqYanChang);
                    }
                    else {
                        drawLine(g2d, xXiaGqCmos, yXiaGqCmos, xXiaGqYanChang, yXiaGqYanChang);
                    }
                }
            }


            g2d.setStroke(dash);

            //平行线
            if(cbPingXing.isSelected()) {
                drawLine(g2d, xObject, yObject, xPx, yPx);
                if(image.x < xPxCmos) {
                    drawLine(g2d, xPx, yPx, imageFix.x, imageFix.y);
                }
                else {
                    drawLine(g2d, xPx, yPx, xPxCmos, yPxCmos);
                }
                if(cbYanChang.isSelected()) {
                    if(image.x < xPxCmos) {
                        drawLine(g2d, image.x, image.y, xPxYanChang, yPxYanChang);
                    }
                    else {
                        drawLine(g2d, xPxCmos, yPxCmos, xPxYanChang, yPxYanChang);
                    }
                }
            }

            double dMiSan = yXiaGqCmos > yShangGqCmos ? yXiaGqCmos - yShangGqCmos : yShangGqCmos - yXiaGqCmos;

            if(jing == JING_WU) {
                this.dMiSan = dMiSan;
                duijiaoWucha = image.x;

                //成象面
                drawLine(g2d, imageFix.x, 300, imageFix.x, -260);
                drawString(g2d, "成象面", imageFix.x, -260, true);
                //误差线
                drawArrowLine(g2d, imageFix.x, -80, 0, -80, 4);
                //误差指示
                double xWucha = imageFix.x/2f;
                if(xWucha < -75) {
                    xWucha = -75;
                }
                drawLine(g2d, xWucha, -80, -75, -140);
                if(doPaint) {
                    drawString(g2d, "对焦误差"+format(image.x), -120, -140-4);
                }
            }
            else {
                if(jing == QIAN_JING) {
                    this.dQianMiSan = dMiSan;
                }
                else {
                    this.dHouMiSan = dMiSan;
                }

                //画弥散圆
                if(cbMiSan.isSelected()) {
                    drawLine(g2d, xShangGqCmos, yShangGqCmos, xShangGqCmos-100, yShangGqCmos);
                    drawLine(g2d, xXiaGqCmos, yXiaGqCmos, xXiaGqCmos-100, yXiaGqCmos);
                    fillShape(g2d, new Ellipse2D.Double(
                            xShangGqCmos - 100-dMiSan/2f, yShangGqCmos < yXiaGqCmos ? yShangGqCmos : yXiaGqCmos, dMiSan, dMiSan));
                }

                if(jing == QIAN_JING) {
                    if(cbMiSan.isSelected()) {
                        drawString(g2d, "弥散圆"+format(dMiSan),
                                xShangGqCmos - 100, (yShangGqCmos > yXiaGqCmos ? yShangGqCmos : yXiaGqCmos), false);
                    }
                    drawString(g2d, "弥散圆"+format(dMiSan),xGx+100, 110);
                }
                else {
                    if(cbMiSan.isSelected()) {
                        drawString(g2d, "弥散圆"+format(dMiSan),
                                xShangGqCmos - 100, (yShangGqCmos < yXiaGqCmos ? yShangGqCmos : yXiaGqCmos), true);
                    }
                    drawString(g2d, "弥散圆"+format(dMiSan),xGx+250, 110);
                }
            }

            g2d.setStroke(stroke);
        }

        private void paintJingShen(Graphics2D g2d) {
            Stroke stroke = g2d.getStroke();

            double xGx = getDuiJiaoLength()+getFocusLength();
            double jingWuLength = getJingLength(JING_WU);
            double qianJingLength = getJingLength(QIAN_JING);
            double houJingLength = getJingLength(HOU_JING);

            g2d.setStroke(dash);
            g2d.setColor(Color.GREEN);
            if((cbQianJing.isSelected() || cbHouJing.isSelected()) && cbJingWu.isSelected()) {
                drawLine(g2d, jingWuLength, 0, jingWuLength, 40);
            }

            g2d.setStroke(dash);
            g2d.setColor(Color.RED);
            if(cbQianJing.isSelected()) {
                drawLine(g2d, qianJingLength, 0, qianJingLength, 80);
            }
            if(cbQianJing.isSelected() && cbJingWu.isSelected()) {
                double y = 20;
                drawLine(g2d, qianJingLength, y, jingWuLength, y);
                g2d.setStroke(stroke);
                drawArrow(g2d, qianJingLength, y, 8, true, false);
                drawArrow(g2d, jingWuLength, y, 8, true, true);
                double x = (qianJingLength+jingWuLength)/2f;
                drawString(g2d, "前景深"+(int)Math.abs(jingWuLength-qianJingLength), x, y, true);
                drawString(g2d, "前景深"+(int)Math.abs(jingWuLength-qianJingLength),xGx+100, 130);
            }

            g2d.setStroke(dash);
            g2d.setColor(Color.BLUE);
            if(cbHouJing.isSelected()) {
                drawLine(g2d, houJingLength, 0, houJingLength, 80);
            }
            if(cbHouJing.isSelected() && cbJingWu.isSelected()) {
                double y = 40;
                drawLine(g2d, jingWuLength, y, houJingLength, y);
                g2d.setStroke(stroke);
                drawArrow(g2d, jingWuLength, y, 8, true, false);
                drawArrow(g2d, houJingLength, y, 8, true, true);
                double x = (jingWuLength+houJingLength)/2f;
                drawString(g2d, "后景深"+(int)Math.abs(houJingLength-jingWuLength), x, y, true);
                drawString(g2d, "后景深"+(int)Math.abs(houJingLength-jingWuLength),xGx+250, 130);
            }

            if(cbQianJing.isSelected() && cbHouJing.isSelected()) {
                g2d.setStroke(dash);
                g2d.setColor(Color.GREEN);
                double y = 65;
                drawLine(g2d, qianJingLength, y, houJingLength, y);
                drawArrow(g2d, qianJingLength, y, 8, true, false);
                drawArrow(g2d, houJingLength, y, 8, true, true);
                double x = (qianJingLength+houJingLength)/2f;
                drawString(g2d, "景深"+(int)Math.abs(houJingLength-qianJingLength), x, y, true);
                drawString(g2d, "景深"+(int)Math.abs(houJingLength-qianJingLength),xGx+400, 130);
            }

            g2d.setStroke(stroke);
        }

        //******************************************************************************************************************

        public void drawLine(
                Graphics2D g2d, double x1, double y1, double x2, double y2) {
            if(!doPaint) {
                return;
            }

            g2d.draw(new Line2D.Double(x1, y1, x2, y2));
        }

        public void drawArrowLine(
                Graphics2D g2d, double x1, double y1, double x2, double y2, double length) {
            if(!doPaint) {
                return;
            }

            g2d.draw(new Line2D.Double(x1, y1, x2, y2));
            double w = length * 0.45;

            //x轴
            if(y1 == y2) {
                double xLeft = x1;
                double xRight = x2;
                if(xLeft > xRight) {
                    xLeft = x2;
                    xRight = x1;
                }

                if(xRight-xLeft < length*3) {
                    return;
                }

                //左
                g2d.draw(new Line2D.Double(xLeft+length, y1-w, xLeft, y1));
                g2d.draw(new Line2D.Double(xLeft+length, y1+w, xLeft, y1));
                //右
                g2d.draw(new Line2D.Double(xRight-length, y1-w, xRight, y1));
                g2d.draw(new Line2D.Double(xRight-length, y1+w, xRight, y1));
            }
            //y轴
            else if(x1 == x2) {
                double yShang = y1;
                double yXia = y2;
                if(yShang > yXia) {
                    yShang = y2;
                    yXia = y1;
                }

                if(yXia-yShang < length*3) {
                    return;
                }

                //上
                g2d.draw(new Line2D.Double(x1-w, yShang+length, x1, yShang));
                g2d.draw(new Line2D.Double(x1+w, yShang+length, x1, yShang));
                //下
                g2d.draw(new Line2D.Double(x1-w, yXia-length, x1, yXia));
                g2d.draw(new Line2D.Double(x1+w, yXia-length, x1, yXia));
            }
        }

        public void drawArrow (
                Graphics2D g2d, double x, double y, double length, boolean isXaxis, boolean isPositive) {
            if(!doPaint) {
                return;
            }

            double w = length * 0.45;

            double x1, x2, y1, y2;
            if(isXaxis) {
                if(isPositive) {
                    x1 = x2 = x - length;
                }
                else {
                    x1 = x2 = x + length;
                }

                y1 = y - w;
                y2 = y + w;
            }
            else {
                if(isPositive) {
                    y1 = y2 = y - length;
                }
                else {
                    y1 = y2 = y + length;
                }

                x1 = x - w;
                x2 = x + w;
            }

            drawLine(g2d, x, y, x1, y1);
            drawLine(g2d, x, y, x2, y2);
        }

        public void fillShape(Graphics2D g2d, Shape shape) {
            if(!doPaint) {
                return;
            }

            g2d.fill(shape);
        }

        public void drawString(Graphics2D g2d, String str, double x, double y) {
            TextLayout layout = new TextLayout(str, g2d.getFont(), FRC);
            layout.draw(g2d, (float)x, (float)y);
        }

        public void drawString(Graphics2D g2d, String str, double x, double y, boolean above) {
            drawString(g2d, new String[]{str}, x, y, above);
        }

        public void drawString(Graphics2D g2d, String[] strs, double x, double y, boolean above) {
            if(!doPaint) {
                return;
            }

            for(String str :strs) {
                TextLayout layout = new TextLayout(str, g2d.getFont(), FRC);
                double tw = layout.getBounds().getWidth();
                double th = layout.getBounds().getHeight();
                float x1 = (float)(x-tw/2f);
                float y1;
                if(above) {
                    y1 = (float) (y - 4);
                }
                else {
                    y1 = (float) (y + th + 4);
                }

                layout.draw(g2d, x1, y1);

                if(above) {
                    y = y1 - th;
                }
                else {
                    y = y1;
                }
            }
        }

    } // end painter

    public static String format(double v) {
        return format(v, 5);
    }

    public static String format(double v, int f) {
        df.setMaximumFractionDigits(f);
        df.setMinimumFractionDigits(f);
        df.setGroupingUsed(false);
        return df.format(v);
    }

    /**
     * 已知直线上的两点A(x1, y2), B(x2, y2)，求该直线上已知x坐标的C(x, ?)点的y坐标
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param x
     * @return C点的y坐标
     */
    public static double calcY(double x1, double y1, double x2, double y2, double x) {
        if(x1 == x2 && y1 == y2) {
            throw new IllegalArgumentException("(x1, y1)与(x2, y2)是同一个点，一个点不能确定一条直线！");
        }
        if(x1 == x2) {
            throw new IllegalArgumentException("x1 == x2是平行于y轴的直线，没有固定的y值！");
        }

        double rate = (y2-y1)/(x2-x1);
        return (x - x1)*rate + y1;
    }

    /**
     * 求两直线的交点
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param x3
     * @param y3
     * @param x4
     * @param y4
     * @return 两直线的交点
     */
    public static Point2D.Double calcIntersectionPoint(
            double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
        //第一条直线
        double a = (y1 - y2) / (x1 - x2);
        double b = (x1 * y2 - x2 * y1) / (x1 - x2);

        //第二条
        double c = (y3 - y4) / (x3 - x4);
        double d = (x3 * y4 - x4 * y3) / (x3 - x4);

        double x = ((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1))
                / ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4));

        double y = ((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4))
                / ((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4));

        return new Point2D.Double(x, y);
    }

}
